<?php
// +----------------------------------------------------------------------
// | XShop小程序商城
// +----------------------------------------------------------------------
// | Author: Peak Xin <xinyflove@gmail.com>
// +----------------------------------------------------------------------

namespace app\api\service;

use app\api\model\Order as OrderModel;
use app\api\service\Order as OrderService;
use app\lib\enum\OrderStatusEnum;
use app\lib\exception\OrderException;
use app\lib\exception\TokenException;
use think\Exception;
use think\Loader;
use think\Log;

// extend/WxPay/WxPay.Api.php
Loader::import('WxPay.WxPay#Api', EXTEND_PATH, '.php');
// extend/WxPay/WxPay.Config.php
Loader::import('WxPay.WxPay#Config', EXTEND_PATH, '.php');
class Pay {

    private $orderID;
    private $orderNO;

    public function __construct($orderID)
    {
        if(!$orderID)
        {
            throw new Exception('订单号不允许为NULL');
        }
        $this->orderID = $orderID;
    }

    /**
     * 付款
     * @return array|\成功时返回
     * @throws OrderException
     * @throws TokenException
     */
    public function pay()
    {
        $this->__checkOrderValid();
        // 库存量检测
        $orderService = new OrderService();
        $status = $orderService->checkOrderStock($this->orderID);
        if(!$status['pass'])
        {
            return $status;
        }

        return $this->__makeWxPreOrder($status['orderPrice']);
    }

    /**
     * 生成微信预支付订单
     * @param $totalPrice
     * @return array|\成功时返回
     * @throws Exception
     * @throws TokenException
     */
    private function __makeWxPreOrder($totalPrice)
    {
        $openid = Token::getCurrentTokenVar('openid');
        if(!$openid)
        {
            throw new TokenException();
        }

        $wxOrderData = new \WxPayUnifiedOrder();
        $wxOrderData->SetOut_trade_no($this->orderNO);
        $wxOrderData->SetTrade_type('JSAPI');
        $wxOrderData->SetTotal_fee($totalPrice * 100);
        $wxOrderData->SetBody('零食商贩');
        $wxOrderData->SetOpenid($openid);
        $wxOrderData->SetNotify_url(config('secure.pay_back_url'));

        $config = new \WxPayConfig();
        return $this->__getPaySignature($config, $wxOrderData);
    }

    /**
     * 获取支付参数
     * @param $config
     * @param $wxOrderData
     * @return array|\成功时返回
     * @throws \WxPayException
     */
    private function __getPaySignature($config, $wxOrderData)
    {
        $wxOrder = \WxPayApi::unifiedOrder($config, $wxOrderData);
        if ($wxOrder['return_code'] != 'SUCCESS' || $wxOrder['result_code'] != 'SUCCESS')
        {
            Log::record($wxOrder, 'error');
            Log::record('获取预支付订单失败', 'error');
            return $wxOrder;
        }

        $this->__recordPreOrder($wxOrder);
        $signature = $this->__sign($wxOrder);
        return $signature;
    }

    /**
     * 生成支付参数
     * @param $wxOrder
     * @return array
     * @throws \WxPayException
     */
    private function __sign($wxOrder)
    {
        $jsApiPayData = new \WxPayJsApiPay();
        $jsApiPayData->SetAppid(config('wx.app_id'));
        $jsApiPayData->SetTimeStamp((string)time());

        $rand = md5(time() . mt_rand(0, 1000));
        $jsApiPayData->SetNonceStr($rand);
        $jsApiPayData->SetPackage('prepay_id='.$wxOrder['prepay_id']);
        $jsApiPayData->SetSignType('md5');
        $sign = $jsApiPayData->MakeSign();
        $rawValues = $jsApiPayData->GetValues();
        $rawValues['paySign'] = $sign;
        unset($rawValues['appId']);

        return $rawValues;
    }

    /**
     * prepay_id保存到数据库中
     * @param $wxOrder
     */
    private function __recordPreOrder($wxOrder)
    {
        OrderModel::where('id', '=', $this->orderID)->update(['prepay_id'=>$wxOrder['prepay_id']]);
    }

    /**
     * 检查订单是否有效
     * @return bool
     * @throws Exception
     * @throws OrderException
     * @throws TokenException
     */
    private function __checkOrderValid()
    {
        // 检查订单是否存在
        $order = OrderModel::where('id', '=', $this->orderID)->find();
        if(!$order)
        {
            throw new OrderException();
        }

        // 检查订单与用户是否匹配
        if(!Token::isValidOperate($order->user_id))
        {
            throw new TokenException([
                'msg' => '订单与用户不匹配',
                'errorCode' => 10003,
            ]);
        }

        // 检查订单支付状态
        if($order->status != OrderStatusEnum::UNPAID)
        {
            throw new OrderException([
                'msg' => '订单已支付过啦',
                'errorCode' => 80003,
                'code' => 400,
            ]);
        }

        $this->orderNO = $order->order_no;
        return true;
    }
}